﻿namespace ImageProcessing
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Drawing;
    using System.Drawing.Imaging;


    /// <summary>
    /// PixelOperation for all operation on pixel
    /// </summary>
    public class PixelOperation
    {
        /// <summary>
        /// Corrects the Gamma value of the image.
        /// </summary>
        /// <param name="oldImage">The old image.</param>
        /// <param name="gamaValue">The gama value.</param>
        /// <returns>new image</returns>
        public static ImageStructure[,] GammaCorrection(ImageStructure[,] oldImage, double gamaValue)
        {
            int height = oldImage.GetLength(0);
            int width = oldImage.GetLength(1);

            ImageStructure[,] newIamge = new ImageStructure[height, width];
            double[,] tempR = new double[height, width];
            double[,] tempG = new double[height, width];
            double[,] tempB = new double[height, width];

            try
            {
                for (int i = 0; i < height; i++)
                {
                    for (int j = 0; j < width; j++)
                    {
                        tempB[i, j] = Math.Pow(Convert.ToDouble(oldImage[i, j].B), 1 / gamaValue);
                        tempG[i, j] = Math.Pow(Convert.ToDouble(oldImage[i, j].G), 1 / gamaValue);
                        tempR[i, j] = Math.Pow(Convert.ToDouble(oldImage[i, j].R), 1 / gamaValue);
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.LogException(ex);
            }

            newIamge = PixelOperation.Normalization(newIamge, height, width, tempR, tempG, tempB);

            return newIamge;
        }

        /// <summary>
        /// Normalizations the specified new image.
        /// </summary>
        /// <param name="newIamge">The new image.</param>
        /// <param name="height">The height.</param>
        /// <param name="width">The width.</param>
        /// <param name="tempR">The temp R.</param>
        /// <param name="tempG">The temp G.</param>
        /// <param name="tempB">The temp B.</param>
        /// <returns>Normalized image</returns>
        public static ImageStructure[,] Normalization(ImageStructure[,] newIamge, int height, int width, double[,] tempR, double[,] tempG, double[,] tempB)
        {
            double[] min = new double[3]; // o R 1 G 2 B
            double[] max = new double[3];
            PixelOperation.GetMinMaxValues(height, width, tempR, tempG, tempB, min, max);

            try
            {
                for (int i = 0; i < height; i++)
                {
                    for (int j = 0; j < width; j++)
                    {
                        newIamge[i, j].B = Convert.ToByte(((tempB[i, j] - min[2]) / (max[2] - min[2])) * 255);
                        newIamge[i, j].G = Convert.ToByte(((tempG[i, j] - min[1]) / (max[1] - min[1])) * 255);
                        newIamge[i, j].R = Convert.ToByte(((tempR[i, j] - min[0]) / (max[0] - min[0])) * 255);
                    }
                }
            }
            catch (Exception e)
            {
                Logger.LogException(e);
            }

            return newIamge;
        }

        /// <summary>
        /// Normalizationbitmaps the specified height.
        /// </summary>
        /// <param name="height">The height.</param>
        /// <param name="width">The width.</param>
        /// <param name="tempR">The temp R.</param>
        /// <param name="tempG">The temp G.</param>
        /// <param name="tempB">The temp B.</param>
        /// <returns>normlized image bitmap</returns>
        public static Bitmap Normalizationbitmap(int height, int width, double[,] tempR, double[,] tempG, double[,] tempB)
        {
            double[] min = new double[3]; // o R 1 G 2 B
            double[] max = new double[3];
            PixelOperation.GetMinMaxValues(height, width, tempR, tempG, tempB, min, max);
            Bitmap newimage = new Bitmap(width, height, PixelFormat.Format24bppRgb);
            int originalPixelIndex;
            unsafe
            {
                BitmapData bmpNewD = newimage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                byte* pNew = (byte*)bmpNewD.Scan0;
                int newExtraBytes = bmpNewD.Stride - (width * 3);
                
                try
                {
                    for (int i = 0; i < height; i++)
                    {
                        for (int j = 0; j < width; j++)
                        {
                            originalPixelIndex = (i * width * 3) + (i * newExtraBytes) + (j * 3);
                            pNew[originalPixelIndex] = Convert.ToByte(((tempR[i, j] - min[0]) / (max[0] - min[0])) * 255);
                            pNew[originalPixelIndex+1] = Convert.ToByte(((tempG[i, j] - min[1]) / (max[1] - min[1])) * 255);
                            pNew[originalPixelIndex+2] = Convert.ToByte(((tempB[i, j] - min[2]) / (max[2] - min[2])) * 255);
                        }
                    }
                    newimage.UnlockBits(bmpNewD);
                }
                catch (Exception e)
                {
                    Logger.LogException(e);
                }
            }
            return newimage;
        }


        /// <summary>
        /// Normalizations the color of the one.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="height">The height.</param>
        /// <param name="width">The width.</param>
        /// <param name="color">The color.</param>
        /// <returns>Normalized image</returns>
        public static ImageStructure[,] NormalizationOneColor(double[,] image, int height, int width, char color)
        {
            double max = double.MinValue, min = double.MaxValue;
            ImageStructure[,] newImage = new ImageStructure[height, width];
            try
            {
                for (int i = 0; i < height; i++)
                {
                    for (int j = 0; j < width; j++)
                    {
                        max = Math.Max(max, image[i, j]);
                        min = Math.Min(min, image[i, j]);
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.LogException(ex);
            }

            try
            {
                if (color == 'r' || color == 'R')
                {
                    for (int i = 0; i < height; i++)
                    {
                        for (int j = 0; j < width; j++)
                        {
                            newImage[i, j].R = Convert.ToByte(((image[i, j] - min) / (max - min)) * 255);
                        }
                    }
                }
                else if (color == 'g' || color == 'G')
                {
                    for (int i = 0; i < height; i++)
                    {
                        for (int j = 0; j < width; j++)
                        {
                            newImage[i, j].G = Convert.ToByte(((image[i, j] - min) / (max - min)) * 255);
                        }
                    }
                }
                else if (color == 'b' || color == 'B')
                {
                    for (int i = 0; i < height; i++)
                    {
                        for (int j = 0; j < width; j++)
                        {
                            newImage[i, j].B = Convert.ToByte(((image[i, j] - min) / (max - min)) * 255);
                        }
                    }
                }
                else
                {
                    for (int i = 0; i < height; i++)
                    {
                        for (int j = 0; j < width; j++)
                        {
                            newImage[i, j].B = Convert.ToByte(((image[i, j] - min) / (max - min)) * 255);
                            newImage[i, j].G = Convert.ToByte(((image[i, j] - min) / (max - min)) * 255);
                            newImage[i, j].R = Convert.ToByte(((image[i, j] - min) / (max - min)) * 255);
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Logger.LogException(e);
            }

            return newImage;
        }

        /// <summary>
        /// Modifies the brightness of the image.
        /// </summary>
        /// <param name="oldImage">The old image.</param>
        /// <param name="brightnessValue">The brightness value.</param>
        /// <returns>Modified Image</returns>
        public static ImageStructure[,] BrightnessCorrection(ImageStructure[,] oldImage, int brightnessValue)
        {
            int height = oldImage.GetLength(0);
            int width = oldImage.GetLength(1);
            ImageStructure[,] newImage = new ImageStructure[height, width];
            int rowsPerThread = height / Environment.ProcessorCount;
            Thread t = null;
            int minHeight, maxHeight;
            Semaphore s = new Semaphore(1, 1000);
            List<Thread> threadList = new List<Thread>();
            for (int i = 1; i < Environment.ProcessorCount; i++)
            {
                s.WaitOne();
                minHeight = i * rowsPerThread;
                maxHeight = (i + 1) * rowsPerThread;
                if (i == (Environment.ProcessorCount - 1))
                {
                    t = new Thread(delegate() { new PixelOperation().BrightnessCorrectionThreading(s, minHeight, height, oldImage, newImage, height, width, brightnessValue); });
                    t.Start();
                    threadList.Add(t);
                    break;
                }

                t = new Thread(delegate() { new PixelOperation().BrightnessCorrectionThreading(s, minHeight, maxHeight, oldImage, newImage, height, width, brightnessValue); });
                t.Start();
                threadList.Add(t);
            }

            new PixelOperation().BrightnessCorrectionThreading(s, 0, rowsPerThread, oldImage, newImage, height, width, brightnessValue);

            foreach (Thread thread in threadList)
            {
                thread.Join();
            }

            return newImage;
        }

        /// <summary>
        /// Edits contrast ratio.
        /// </summary>
        /// <param name="originalImage">The original image.</param>
        /// <param name="contrastValue">The contrast value.</param>
        /// <returns>returns contrasted image.</returns>
        public static ImageStructure[,] ContrastCorrection(ImageStructure[,] originalImage, int contrastValue)
        {
            int height = originalImage.GetLength(0);
            int width = originalImage.GetLength(1);

            ImageStructure[,] newImage = new ImageStructure[height, width];

            ImageStructure min = new ImageStructure();
            ImageStructure max = new ImageStructure();

            GetMinMaxValuesByte(originalImage, height, width, ref min, ref max);

            int rowsPerThread = height / Environment.ProcessorCount;
            Thread t = null;
            int minHeight, maxHeight;
            Semaphore s = new Semaphore(1, 1000);
            List<Thread> threadList = new List<Thread>();
            for (int i = 1; i < Environment.ProcessorCount; i++)
            {
                s.WaitOne();
                minHeight = i * rowsPerThread;
                maxHeight = (i + 1) * rowsPerThread;
                if (i == (Environment.ProcessorCount - 1))
                {
                    t = new Thread(delegate() { new PixelOperation().ContrastCorrectionThreading(s, minHeight, height, originalImage, newImage, height, width, contrastValue, min, max); });
                    t.Start();
                    threadList.Add(t);
                    break;
                }

                t = new Thread(delegate() { new PixelOperation().ContrastCorrectionThreading(s, minHeight, maxHeight, originalImage, newImage, height, width, contrastValue, min, max); });
                t.Start();
                threadList.Add(t);
            }

            new PixelOperation().ContrastCorrectionThreading(s, 0, rowsPerThread, originalImage, newImage, height, width, contrastValue, min, max);

            foreach (Thread thread in threadList)
            {
                thread.Join();
            }

            return newImage;
        }

        /// <summary>
        /// Nots the operation.
        /// </summary>
        /// <param name="oldImage">The old image.</param>
        /// <returns>new image after not operation</returns>
        public static ImageStructure[,] NotOperation(ImageStructure[,] oldImage)
        {
            int heigth = oldImage.GetLength(0);
            int width = oldImage.GetLength(1);
            ImageStructure[,] newImage = new ImageStructure[heigth, width];

            for (int i = 0; i < heigth; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    newImage[i, j].B = Convert.ToByte(byte.MaxValue - oldImage[i, j].B);
                    newImage[i, j].R = Convert.ToByte(byte.MaxValue - oldImage[i, j].R);
                    newImage[i, j].G = Convert.ToByte(byte.MaxValue - oldImage[i, j].G);
                }
            }

            return newImage;
        }

        /// <summary>
        /// Cuts the off image.
        /// </summary>
        /// <param name="red">The red.</param>
        /// <param name="green">The green.</param>
        /// <param name="blue">The blue.</param>
        /// <returns>Cutoff image</returns>
        public static ImageStructure[,] CutOffImage(double[,] red, double[,] green, double[,] blue)
        {
            int height = red.GetLength(0);
            int width = red.GetLength(1);
            ImageStructure[,] newImage = new ImageStructure[height, width];
            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    newImage[i, j].R = (byte)NeighborhoodOperations.CutOff(red[i, j], 0, 255);
                    newImage[i, j].G = (byte)NeighborhoodOperations.CutOff(green[i, j], 0, 255);
                    newImage[i, j].B = (byte)NeighborhoodOperations.CutOff(blue[i, j], 0, 255);
                }
            }

            return newImage;
        }

        /// <summary>
        /// Thres the shold white black.
        /// </summary>
        /// <param name="oldImage">The old image.</param>
        /// <param name="thresholdValue">The threshold value.</param>
        /// <returns>Thresholded image</returns>
        public static ImageStructure[,] ThreSholdWhiteBlack(ImageStructure[,] oldImage, byte thresholdValue,bool r,bool g,bool b)
        {
            int heigth = oldImage.GetLength(0);
            int width = oldImage.GetLength(1);
            ImageStructure[,] newImage = new ImageStructure[heigth, width];

            for (int i = 0; i < heigth; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    if (r)
                    {
                        if (oldImage[i, j].R < thresholdValue)
                        {
                            newImage[i, j].R = 0;
                        }
                        else
                        {
                            newImage[i, j].R = 255;
                        }
                    }

                    if (g)
                    {
                        if (oldImage[i, j].G < thresholdValue)
                        {
                            newImage[i, j].G = 0;
                        }
                        else
                        {
                            newImage[i, j].G = 255;
                        }
                    }

                    if (b)
                    {
                        if (oldImage[i, j].B < thresholdValue)
                        {
                            newImage[i, j].B = 0;
                        }
                        else
                        {
                            newImage[i, j].B = 255;
                        }
                    }
                }
            }

            return newImage;
        }

        public static ImageStructure[,] ThreSholdWhiteBlack(ImageStructure[,] oldImage, byte thresholdValuer, byte thresholdValueg, byte thresholdValueb)
        {
            int heigth = oldImage.GetLength(0);
            int width = oldImage.GetLength(1);
            ImageStructure[,] newImage = new ImageStructure[heigth, width];

            for (int i = 0; i < heigth; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    
                        if (oldImage[i, j].R < thresholdValuer)
                        {
                            newImage[i, j].R = 0;
                        }
                        else
                        {
                            newImage[i, j].R = 255;
                        }
       
                        if (oldImage[i, j].G < thresholdValueg)
                        {
                            newImage[i, j].G = 0;
                        }
                        else
                        {
                            newImage[i, j].G = 255;
                        }
                   

                        if (oldImage[i, j].B < thresholdValueb)
                        {
                            newImage[i, j].B = 0;
                        }
                        else
                        {
                            newImage[i, j].B = 255;
                        }
                   
                }
            }

            return newImage;
        }


        /// <summary>
        /// Thres the shold binary.
        /// </summary>
        /// <param name="oldImage">The old image.</param>
        /// <param name="thresholdValue">The threshold value.</param>
        /// <returns></returns>
        public static ImageStructure[,] ThreSholdBinary(ImageStructure[,] oldImage, byte thresholdValue)
        {
            ImageStructure[,] grayImage = Operations.GreyScale(oldImage);
            int heigth = grayImage.GetLength(0);
            int width = grayImage.GetLength(1);
            ImageStructure[,] newImage = new ImageStructure[heigth, width];

            for (int i = 0; i < heigth; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    if (grayImage[i, j].R < thresholdValue)
                    {
                        newImage[i, j].R = 0;
                    }
                    else
                    {
                        newImage[i, j].R = 255;
                    }

                    if (grayImage[i, j].G < thresholdValue)
                    {
                        newImage[i, j].G = 0;
                    }
                    else
                    {
                        newImage[i, j].G = 255;
                    }

                    if (grayImage[i, j].B < thresholdValue)
                    {
                        newImage[i, j].B = 0;
                    }
                    else
                    {
                        newImage[i, j].B = 255;
                    }
                }
            }

            return newImage;
        }

        /// <summary>
        /// Gets the max value.
        /// </summary>
        /// <param name="height">The height.</param>
        /// <param name="width">The width.</param>
        /// <param name="tempR">The temp R.</param>
        /// <param name="tempG">The temp G.</param>
        /// <param name="tempB">The temp B.</param>
        /// <param name="min">The min.</param>
        /// <param name="max">The max.</param>
        private static void GetMinMaxValues(int height, int width, double[,] tempR, double[,] tempG, double[,] tempB, double[] min, double[] max)
        {
            double tempMaxR = 0;
            double tempMaxG = 0;
            double tempMaxB = 0;

            double tempMinR = 255;
            double tempMinG = 255;
            double tempMinB = 255;

            try
            {
                for (int i = 0; i < height; i++)
                {
                    for (int j = 0; j < width; j++)
                    {
                        tempMaxR = (tempR[i, j] > tempMaxR) ? tempR[i, j] : tempMaxR;
                        tempMaxG = (tempG[i, j] > tempMaxG) ? tempG[i, j] : tempMaxG;
                        tempMaxB = (tempB[i, j] > tempMaxB) ? tempB[i, j] : tempMaxB;

                        tempMinR = (tempR[i, j] < tempMinR) ? tempR[i, j] : tempMinR;
                        tempMinG = (tempG[i, j] < tempMinG) ? tempG[i, j] : tempMinG;
                        tempMinB = (tempB[i, j] < tempMinB) ? tempB[i, j] : tempMinB;
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.LogException(ex);
            }

            min[0] = tempMinR;
            min[1] = tempMinG;
            min[2] = tempMinB;
            max[0] = tempMaxR;
            max[1] = tempMaxG;
            max[2] = tempMaxB;
        }

        /// <summary>
        /// Gets the max value, returns bytes.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="height">The height.</param>
        /// <param name="width">The width.</param>
        /// <param name="min">The min.</param>
        /// <param name="max">The max.</param>
        private static void GetMinMaxValuesByte(ImageStructure[,] image, int height, int width, ref ImageStructure min, ref ImageStructure max)
        {
            byte tempMax;
            byte tempMin;

            max.R = byte.MinValue;
            max.G = byte.MinValue;
            max.B = byte.MinValue;

            min.R = byte.MaxValue;
            min.G = byte.MaxValue;
            min.B = byte.MaxValue;

            try
            {
                for (int i = 0; i < height; i++)
                {
                    for (int j = 0; j < width; j++)
                    {
                        tempMax = Math.Max(max.R, image[i, j].R);
                        max.R = (tempMax > max.R) ? tempMax : max.R;

                        tempMax = Math.Max(max.G, image[i, j].G);
                        max.G = (tempMax > max.G) ? tempMax : max.G;

                        tempMax = Math.Max(max.B, image[i, j].B);
                        max.B = (tempMax > max.B) ? tempMax : max.B;

                        tempMin = Math.Min(min.R, image[i, j].R);
                        min.R = (tempMin < min.R) ? tempMin : min.R;

                        tempMin = Math.Min(min.G, image[i, j].G);
                        min.G = (tempMin < min.G) ? tempMin : min.G;

                        tempMin = Math.Min(min.B, image[i, j].B);
                        min.B = (tempMin < min.B) ? tempMin : min.B;
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.LogException(ex);
            }
        }

        /// <summary>
        /// Threading function for brightness editing.
        /// </summary>
        /// <param name="s">Semaphore s.</param>
        /// <param name="minHeight">Minimum height to start from.</param>
        /// <param name="maxHeight">Max height to stop at.</param>
        /// <param name="oldImage">The old image.</param>
        /// <param name="newImage">The new image.</param>
        /// <param name="height">The image height.</param>
        /// <param name="width">The image width.</param>
        /// <param name="brightnessValue">The brightness value.</param>
        private void BrightnessCorrectionThreading(Semaphore s, int minHeight, int maxHeight, ImageStructure[,] oldImage, ImageStructure[,] newImage, int height, int width, int brightnessValue)
        {
            s.Release();
            try
            {
                for (int i = minHeight; i < maxHeight; i++)
                {
                    for (int j = 0; j < width; j++)
                    {
                        // Red
                        if ((oldImage[i, j].R + brightnessValue) > 255)
                        {
                            newImage[i, j].R = 255;
                        }
                        else if ((oldImage[i, j].R + brightnessValue) < 0)
                        {
                            newImage[i, j].R = 0;
                        }
                        else
                        {
                            newImage[i, j].R = (byte)(brightnessValue + oldImage[i, j].R);
                        }

                        // Green
                        if ((oldImage[i, j].G + brightnessValue) > 255)
                        {
                            newImage[i, j].G = 255;
                        }
                        else if ((oldImage[i, j].G + brightnessValue) < 0)
                        {
                            newImage[i, j].G = 0;
                        }
                        else
                        {
                            newImage[i, j].G = (byte)(brightnessValue + oldImage[i, j].G);
                        }

                        // Blue
                        if ((oldImage[i, j].B + brightnessValue) > 255)
                        {
                            newImage[i, j].B = 255;
                        }
                        else if ((oldImage[i, j].B + brightnessValue) < 0)
                        {
                            newImage[i, j].B = 0;
                        }
                        else
                        {
                            newImage[i, j].B = (byte)(brightnessValue + oldImage[i, j].B);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.LogException(ex);
            }
        }

        /// <summary>
        /// Threading function for contrast correction.
        /// </summary>
        /// <param name="s">The s.</param>
        /// <param name="minHeight">Height of the min.</param>
        /// <param name="maxHeight">Height of the max.</param>
        /// <param name="originalImage">The original image.</param>
        /// <param name="newImage">The new image.</param>
        /// <param name="height">The height.</param>
        /// <param name="width">The width.</param>
        /// <param name="contrastValue">The contrast value.</param>
        /// <param name="oldMin">The old min.</param>
        /// <param name="oldMax">The old max.</param>
        private void ContrastCorrectionThreading(Semaphore s, int minHeight, int maxHeight, ImageStructure[,] originalImage, ImageStructure[,] newImage, int height, int width, int contrastValue, ImageStructure oldMin, ImageStructure oldMax)
        {
            s.Release();
            ImageStructure newMax = new ImageStructure();
            ImageStructure newMin = new ImageStructure();

            newMax.R = (byte)((oldMax.R + contrastValue) > 255 ? 255 : oldMax.R + contrastValue);
            newMax.G = (byte)((oldMax.G + contrastValue) > 255 ? 255 : oldMax.G + contrastValue);
            newMax.B = (byte)((oldMax.B + contrastValue) > 255 ? 255 : oldMax.B + contrastValue);

            newMin.R = (byte)((oldMin.R - contrastValue) < 0 ? 1 : oldMin.R - contrastValue);
            newMin.G = (byte)((oldMin.G - contrastValue) < 0 ? 1 : oldMin.G - contrastValue);
            newMin.B = (byte)((oldMin.B - contrastValue) < 0 ? 1 : oldMin.B - contrastValue);

            try
            {
                double tempByte = 0;
                for (int i = minHeight; i < maxHeight; i++)
                {
                    for (int j = 0; j < width; j++)
                    {
                        // Red
                        tempByte = (double)(((double)(originalImage[i, j].R - oldMin.R) / (double)(oldMax.R - oldMin.R)) * (double)(newMax.R - newMin.R)) + (double)newMin.R;
                        if (tempByte > 255)
                        {
                            newImage[i, j].R = 255;
                        }
                        else if (tempByte < 0)
                        {
                            newImage[i, j].R = 0;
                        }
                        else
                        {
                            newImage[i, j].R = (byte)tempByte;
                        }

                        // Green
                        tempByte = (double)(((double)(originalImage[i, j].G - oldMin.G) / (double)(oldMax.G - oldMin.G)) * (double)(newMax.G - newMin.G)) + (double)newMin.G;
                        if (tempByte > 255)
                        {
                            newImage[i, j].G = 255;
                        }
                        else if (tempByte < 0)
                        {
                            newImage[i, j].G = 0;
                        }
                        else
                        {
                            newImage[i, j].G = (byte)tempByte;
                        }

                        // Blue
                        tempByte = (double)(((double)(originalImage[i, j].B - oldMin.B) / (double)(oldMax.B - oldMin.B)) * (double)(newMax.B - newMin.B)) + (double)newMin.B;
                        if (tempByte > 255)
                        {
                            newImage[i, j].B = 255;
                        }
                        else if (tempByte < 0)
                        {
                            newImage[i, j].B = 0;
                        }
                        else
                        {
                            newImage[i, j].B = (byte)tempByte;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.LogException(ex);
            }
        }
    }
}